<!DOCTYPE html>
<html lang=zh>
<head>
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
    <meta name="description" content="在聊响应式之前，我们先来说一说ES6新增的 Proxy 与 Reflect 。 监听对象我们在ES6之前，其实是可以通过 Object.defineProperty() 实现对象的监听操作。 123456789101112Object.keys(obj).forEach(key &#x3D;&gt; &amp;#123;  let value &#x3D; obj[key]  Object.defineProperty(ob">
<meta property="og:type" content="article">
<meta property="og:title" content="响应式原理">
<meta property="og:url" content="https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/index.html">
<meta property="og:site_name" content="柯里的语法糖">
<meta property="og:description" content="在聊响应式之前，我们先来说一说ES6新增的 Proxy 与 Reflect 。 监听对象我们在ES6之前，其实是可以通过 Object.defineProperty() 实现对象的监听操作。 123456789101112Object.keys(obj).forEach(key &#x3D;&gt; &amp;#123;  let value &#x3D; obj[key]  Object.defineProperty(ob">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://codertzm.oss-cn-chengdu.aliyuncs.com/20211214134324.png">
<meta property="article:published_time" content="2022-02-05T06:19:40.056Z">
<meta property="article:modified_time" content="2022-02-05T06:19:40.056Z">
<meta property="article:author" content="柯里的语法糖">
<meta property="article:tag" content="JavaScript">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://codertzm.oss-cn-chengdu.aliyuncs.com/20211214134324.png">
    
    
      
        
          <link rel="shortcut icon" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico">
        
      
      
        
          <link rel="icon" type="image/png" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico" sizes="192x192">
        
      
      
        
          <link rel="apple-touch-icon" sizes="180x180" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico">
        
      
    
    <!-- title -->
    <title>响应式原理</title>
    <!-- styles -->
    
<link rel="stylesheet" href="/css/style.css">

    <!-- persian styles -->
    
    <!-- rss -->
    
    
	<!-- mathjax -->
	
<meta name="generator" content="Hexo 6.0.0"></head>

<body class="max-width mx-auto px3 ltr">
    
      <div id="header-post">
  <a id="menu-icon" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" aria-label="Top" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
        <!--
       --><li><a href="/">首页</a></li><!--
     --><!--
       --><li><a href="/tags/">标签</a></li><!--
     --><!--
       --><li><a href="/categories/">分类</a></li><!--
     --><!--
       --><li><a href="/archives/">归档</a></li><!--
     --><!--
       --><li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li><!--
     --><!--
       --><li><a href="/about/">关于</a></li><!--
     --><!--
       --><li><a href="/search/">搜索</a></li><!--
     -->
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        <li><a class="icon" aria-label="上一篇" href="/2022/02/05/JavaScript/throttle/"><i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i></a></li>
        
        
        <li><a class="icon" aria-label="下一篇" href="/2022/02/05/JavaScript/functionalProgramming/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" aria-label="返回顶部" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" aria-label="分享文章" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">上一篇</span>
      <span id="i-next" class="info" style="display:none;">下一篇</span>
      <span id="i-top" class="info" style="display:none;">返回顶部</span>
      <span id="i-share" class="info" style="display:none;">分享文章</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&text=响应式原理"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&is_video=false&description=响应式原理"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=响应式原理&body=Check out this article: https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&name=响应式原理&description="><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&t=响应式原理"><i class="fab fa-hacker-news " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A1"><span class="toc-number">1.</span> <span class="toc-text">监听对象</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Proxy"><span class="toc-number">2.</span> <span class="toc-text">Proxy</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E5%AE%9E%E7%8E%B0%E7%9B%91%E5%90%AC"><span class="toc-number">2.1.</span> <span class="toc-text">Proxy实现监听</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E7%9A%84%E6%8D%95%E8%8E%B7%E5%99%A8"><span class="toc-number">2.2.</span> <span class="toc-text">Proxy的捕获器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Reflect"><span class="toc-number">3.</span> <span class="toc-text">Reflect</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="toc-number">3.1.</span> <span class="toc-text">Reflect的作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E4%B8%AD%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-number">3.2.</span> <span class="toc-text">Reflect中的方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">3.3.</span> <span class="toc-text">Reflect的使用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E4%B8%8EReflect%E4%B8%AD%E7%9A%84Receiver%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="toc-number">3.4.</span> <span class="toc-text">Proxy与Reflect中的Receiver的作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E4%B8%AD%E7%9A%84construct"><span class="toc-number">3.5.</span> <span class="toc-text">Reflect中的construct</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.</span> <span class="toc-text">响应式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.1.</span> <span class="toc-text">什么是响应式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E5%87%BD%E6%95%B0%E8%AE%BE%E8%AE%A1"><span class="toc-number">4.2.</span> <span class="toc-text">响应式函数设计</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0"><span class="toc-number">4.3.</span> <span class="toc-text">响应式函数实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E4%BE%9D%E8%B5%96%E7%9A%84%E6%94%B6%E9%9B%86"><span class="toc-number">4.4.</span> <span class="toc-text">响应式依赖的收集</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%8F%98%E5%8C%96"><span class="toc-number">4.5.</span> <span class="toc-text">监听对象的变化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86"><span class="toc-number">4.6.</span> <span class="toc-text">对象的依赖管理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">4.7.</span> <span class="toc-text">对象的依赖管理的实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Depend%E5%AF%B9%E8%B1%A1%E7%9A%84%E9%87%8D%E6%9E%84"><span class="toc-number">4.8.</span> <span class="toc-text">Depend对象的重构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E5%93%8D%E5%BA%94%E5%BC%8F%E5%AF%B9%E8%B1%A1"><span class="toc-number">4.9.</span> <span class="toc-text">创建响应式对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue2-%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.10.</span> <span class="toc-text">Vue2 的响应式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">5.</span> <span class="toc-text">总结</span></a></li></ol>
    </div>
  </span>
</div>

    
    <div class="content index py4">
        
        <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        响应式原理
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">柯里的语法糖</span>
      </span>
      
    <div class="postdate">
      
        <time datetime="2022-02-05T06:19:40.056Z" itemprop="datePublished">2022-02-05</time>
        
      
    </div>


      
    <div class="article-category">
        <i class="fas fa-archive"></i>
        <a class="category-link" href="/categories/%E5%89%8D%E7%AB%AF%E5%9F%BA%E7%A1%80/">前端基础</a>
    </div>


      
    <div class="article-tag">
        <i class="fas fa-tag"></i>
        <a class="tag-link-link" href="/tags/JavaScript/" rel="tag">JavaScript</a>
    </div>


    </div>
  </header>
  

  <div class="content" itemprop="articleBody">
    <p>在聊响应式之前，我们先来说一说ES6新增的 <code>Proxy</code> 与 <code>Reflect</code> 。</p>
<h2 id="监听对象"><a href="#监听对象" class="headerlink" title="监听对象"></a>监听对象</h2><p>我们在ES6之前，其实是可以通过 <code>Object.defineProperty()</code> 实现对象的监听操作。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.keys(obj).forEach(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> value = obj[key]</span><br><span class="line">  <span class="built_in">Object</span>.defineProperty(obj,key,&#123;</span><br><span class="line">    <span class="function"><span class="title">set</span>(<span class="params">newValue</span>)</span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`监听到<span class="subst">$&#123;key&#125;</span>的变化`</span>)</span><br><span class="line">      value = newValue</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">get</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">      <span class="keyword">return</span> value</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><strong>但是这样做是有缺点的</strong>：因为 <code>Object.defineProperty()</code> 的设计初衷就不是为了监听对象来设计，其次如果我们想要监听更加丰富的操作：新增、删除等，这个方法是无法实现的。</p>
<h2 id="Proxy"><a href="#Proxy" class="headerlink" title="Proxy"></a>Proxy</h2><p>在ES6中，新增了一个 <code>Proxy</code> 类，这个类从名字就可以看出来，是用于帮助我们创建一个代理的：</p>
<p>也就是说，如果我们希望 <strong>监听一个对象</strong> 的相关操作，那么我们可以先创建一个代理对象（ <code>Proxy</code> 对象）。</p>
<p>之后对该对象的所有操作，都通过代理对象来完成，代理对象可以监听我们想要对原对象进行的操作。</p>
<h3 id="Proxy实现监听"><a href="#Proxy实现监听" class="headerlink" title="Proxy实现监听"></a>Proxy实现监听</h3><p>我们将上面的案例用 <code>Proxy</code> 来实现：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;curry&#x27;</span>,</span><br><span class="line">  <span class="attr">age</span>: <span class="number">33</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建代理对象</span></span><br><span class="line"><span class="comment">// target指的是目标对象，handler指的是包含捕获器的处理器对象</span></span><br><span class="line"><span class="comment">// new Proxy(target,handler)</span></span><br><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;&#125;)</span><br></pre></td></tr></table></figure>

<p>之后我们要操作对象的话，直接操作创建出来的代理对象即可。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;curry&#x27;</span>,</span><br><span class="line">  <span class="attr">age</span>: <span class="number">33</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="function"><span class="title">get</span>(<span class="params">target, key, recevicer</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> target[key]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, recevicer</span>)</span> &#123;</span><br><span class="line">    target[key] = newValue</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">has</span>(<span class="params">target, key, recevicer</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> key <span class="keyword">in</span> target</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">delete</span> target[key]</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="Proxy的捕获器"><a href="#Proxy的捕获器" class="headerlink" title="Proxy的捕获器"></a>Proxy的捕获器</h3><p><code>Proxy</code> 的捕获器一共有13种，常用的捕获器大概有以下几种：</p>
<p><code>handler.has()</code> ：<code>in</code> 操作符的捕捉器。</p>
<p><code>handler.get()</code> ：属性读取操作的捕捉器。</p>
<p><code>handler.set()</code> ：属性设置操作的捕捉器。</p>
<p><code>handler.deleteProperty()</code> ：<code>delete</code> 操作符的捕捉器。</p>
<p><code>handler.apply()</code> ：函数调用操作的捕捉器。</p>
<p><code>handler.construct()</code> ：<code>new</code> 操作符的捕捉器。</p>
<h2 id="Reflect"><a href="#Reflect" class="headerlink" title="Reflect"></a>Reflect</h2><p><code>Reflect</code> 也是ES6新增的一个 <code>API</code> ，它是一个对象，字面的意思是反射。</p>
<h3 id="Reflect的作用"><a href="#Reflect的作用" class="headerlink" title="Reflect的作用"></a>Reflect的作用</h3><p><code>Reflect</code> 提供了很多操作对象的方法，这些方法与 <code>Object</code> 中操作对象的方法类似。</p>
<p>既然有了 <code>Object</code> 中的方法可以做这些操作，<strong>为什么需要新增 <code>Reflect</code> 对象呢？</strong></p>
<p>这是因为在早期的 <code>ECMA规范</code> 没有考虑到这种对 <strong>对象的操作</strong> 如何设计会更加规范，所以将这些 <code>API</code> 放在了 <code>Object</code> 上。</p>
<p>但是 <code>Object</code> 作为构造函数，使用这些操作并不合适。</p>
<p>因此才新增了 <code>Reflect</code> 对象。</p>
<h3 id="Reflect中的方法"><a href="#Reflect中的方法" class="headerlink" title="Reflect中的方法"></a>Reflect中的方法</h3><p><code>Reflect</code> 中的很多方法都与 <code>Object</code> 中的方法类似，而且它的所有方法与 <code>Proxy</code> 的13种捕获器一一对应。</p>
<h3 id="Reflect的使用"><a href="#Reflect的使用" class="headerlink" title="Reflect的使用"></a>Reflect的使用</h3><p>我们可以将 <code>Proxy</code> 中的捕获器中对对象的操作从 <code>Object</code> 中的方法修改为 <code>Reflect</code> 中的方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;curry&#x27;</span>,</span><br><span class="line">  <span class="attr">age</span>: <span class="number">33</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// return target[key]</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target,key)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// target[key] = newValue</span></span><br><span class="line">    <span class="built_in">Reflect</span>.set(target,key,newValue)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">has</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// return key in target</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.has(target,key)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// delete target[key]</span></span><br><span class="line">    <span class="built_in">Reflect</span>.deleteProperty(target,key)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="Proxy与Reflect中的Receiver的作用"><a href="#Proxy与Reflect中的Receiver的作用" class="headerlink" title="Proxy与Reflect中的Receiver的作用"></a>Proxy与Reflect中的Receiver的作用</h3><p>如果我们的源对象有setter、getter的访问器属性，那么可以通过 <code>receiver</code> 来改变里面的 <code>this</code> ，将 <code>this</code> 指向了 <code>proxy</code> 对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  <span class="attr">_name</span>: <span class="string">&#x27;curry&#x27;</span>,</span><br><span class="line">  <span class="attr">age</span>: <span class="number">33</span>,</span><br><span class="line">  <span class="keyword">get</span> <span class="title">name</span>()&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>._name</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="keyword">set</span> <span class="title">name</span>(<span class="params">newValue</span>)&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="built_in">this</span>)</span><br><span class="line">    <span class="built_in">this</span>._name = newValue</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="comment">// receiver是创建出来的代理对象</span></span><br><span class="line">  <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// return target[key]</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;getter&#x27;</span>)</span><br><span class="line">    <span class="comment">// 此时 obj里面的getter中的this指向的是 objProxy对象</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// target[key] = newValue</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;setter&#x27;</span>)</span><br><span class="line">    <span class="built_in">Reflect</span>.set(target, key, newValue, receiver)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">has</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// return key in target</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.has(target, key)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// delete target[key]</span></span><br><span class="line">    <span class="built_in">Reflect</span>.deleteProperty(target, key)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">objProxy.name = <span class="string">&#x27;james&#x27;</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect中的construct"><a href="#Reflect中的construct" class="headerlink" title="Reflect中的construct"></a>Reflect中的construct</h3><p>利用 <code>Reflect.construct(target, argumentsList, newTarget)</code> 方法，可以实现通过一个函数作为构造函数，创建出另外一个类型的操作。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name,age</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">this</span>.name = name</span><br><span class="line">  <span class="built_in">this</span>.age = age</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Coder</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj = <span class="built_in">Reflect</span>.construct(Person,[<span class="string">&#x27;curry&#x27;</span>,<span class="number">33</span>], Coder)</span><br><span class="line"><span class="built_in">console</span>.log(obj.__proto__ == Coder.prototype) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h2 id="响应式"><a href="#响应式" class="headerlink" title="响应式"></a>响应式</h2><p>说完 <code>Proxy</code> 与 <code>Reflect</code> ，我们就可以来看一看响应式，首先来了解，<strong>什么是响应式</strong> 。</p>
<h3 id="什么是响应式"><a href="#什么是响应式" class="headerlink" title="什么是响应式"></a>什么是响应式</h3><p>来看下面这段代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> m = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// m变化时，需要重复执行的代码</span></span><br><span class="line"><span class="built_in">console</span>.log(m)</span><br><span class="line"><span class="built_in">console</span>.log(m + <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">m = <span class="number">40</span></span><br></pre></td></tr></table></figure>

<p>在这个例子中，我们想要实现当变量 <code>m</code> 变化时，输出的那两句代码可以重复执行。像这样的一种可以自动响应数据变量的代码机制，我们称之为是 <strong>响应式</strong> 。</p>
<h3 id="响应式函数设计"><a href="#响应式函数设计" class="headerlink" title="响应式函数设计"></a>响应式函数设计</h3><p>在我们的开发中，会封装很多函数，那么我们需要区分一个函数是否需要响应式。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 需要响应式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">const</span> value = obj.name</span><br><span class="line">  <span class="built_in">console</span>.log(value)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不需要响应式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> res = <span class="number">1</span> + <span class="number">1</span></span><br><span class="line">  <span class="built_in">console</span>.log(res)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>很明显，上面这段代码中的两个函数：</p>
<p>对于 <code>foo</code> 来说，需要在 <code>obj.name</code> 发生变化的时候，重新执行，做出响应。</p>
<p>对于 <code>bar</code> 来说，它是一个完全独立的函数，不需要执行任何的响应式操作。</p>
<h3 id="响应式函数实现"><a href="#响应式函数实现" class="headerlink" title="响应式函数实现"></a>响应式函数实现</h3><p>我们需要封装一个函数，来区别与普通函数，告诉开发者，只要是通过这传入这个函数的函数，都是响应式的函数。其他默认都不是响应式的函数。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> reactiveFns = []</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watchFn</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  reactiveFns.push(fn)</span><br><span class="line">  fn()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">watchFn(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">const</span> value = obj.name</span><br><span class="line">  <span class="built_in">console</span>.log(value)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="响应式依赖的收集"><a href="#响应式依赖的收集" class="headerlink" title="响应式依赖的收集"></a>响应式依赖的收集</h3><p>如果我们仅仅用一个 <code>reactiveFns</code> 的数组来保存所有的响应式函数，是不现实的。因为，在实际开发中，需要很多创建对象，而对象的每一个属性都需要对应自己的一组响应式函数。</p>
<p>因此，我们需要一个类，用来专门收集一个对象的某一个函数的依赖。（替代了原来的 <code>reactiveFns</code> 数组）</p>
<p><code>Depend</code> 类</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Depend</span></span>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.reactiveFns = []</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 添加依赖</span></span><br><span class="line">  <span class="function"><span class="title">addDepend</span>(<span class="params">fn</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.reactiveFns.push(fn)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 做出响应</span></span><br><span class="line">  <span class="function"><span class="title">notify</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.reactiveFns.forEach(<span class="function"><span class="params">fn</span> =&gt;</span> &#123;</span><br><span class="line">      fn()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>watchFn</code> 函数</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> depend = <span class="keyword">new</span> Depend()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watchFn</span>(<span class="params">fn</span>)</span>&#123;</span><br><span class="line">  depend.addDepend(fn)</span><br><span class="line">  fn()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="监听对象的变化"><a href="#监听对象的变化" class="headerlink" title="监听对象的变化"></a>监听对象的变化</h3><p>接下来我们就可以使用 <code>Proxy</code> 与 <code>Reflect</code> 监听对象的变化</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="built_in">Reflect</span>.set(target, key, newValue, receiver)</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="对象的依赖管理"><a href="#对象的依赖管理" class="headerlink" title="对象的依赖管理"></a>对象的依赖管理</h3><p>我们目前创建的 <code>depend</code> 对象，只能用来收集对于 <code>obj</code> 对象的 某一个属性的变化需要监听的响应函数。</p>
<p>但是在开发中，我们需要对不同的对象的不同的属性进行不同的响应。</p>
<p>这个时候，我们需要借助ES6中新增的数据结构：<code>WeakMap</code> 。它对应的数据结构如下图所示：</p>
<p><img src="https://codertzm.oss-cn-chengdu.aliyuncs.com/20211214134324.png" alt="对应的数据结构"></p>
<h3 id="对象的依赖管理的实现"><a href="#对象的依赖管理的实现" class="headerlink" title="对象的依赖管理的实现"></a>对象的依赖管理的实现</h3><p>我们可以封装一个 <code>getDepend</code> 函数，用来管理这种依赖关系</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getDepend</span>(<span class="params">target, key</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> map = targetWeakMap.get(target)</span><br><span class="line">  <span class="keyword">if</span> (!map) &#123;</span><br><span class="line">    map = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">    targetWeakMap.set(target, map)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> depend = map.get(key)</span><br><span class="line">  <span class="keyword">if</span>(!depend)&#123;</span><br><span class="line">    depend = <span class="keyword">new</span> Depend()</span><br><span class="line">    map.set(key,depend)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> depend</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>目前存在的问题是：</p>
<p><code>watchFn</code> 函数这种收集方式根本不知道是哪一个 <code>key</code> 的哪一个 <code>depend</code> 需要收集依赖。</p>
<p>并且只能针对一个单独的 <code>depend</code> 对象来添加你的依赖对象。</p>
<p>所以我们需要做出修改：如果一个函数中使用了某个对象的 <code>key</code> ，那么它就应该被收集</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// watch 函数的修改</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watchFn</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  activeReactiveFn = fn</span><br><span class="line">  fn()</span><br><span class="line">  activeReactiveFn = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// proxy对象捕获器的修改</span></span><br><span class="line"><span class="keyword">const</span> objProxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">  <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="comment">// 获取依赖</span></span><br><span class="line">    <span class="keyword">const</span> depend = getDepend(target,key)</span><br><span class="line">    <span class="comment">// 添加依赖</span></span><br><span class="line">    depend.addDepend(activeReactiveFn)</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, receiver</span>)</span> &#123;</span><br><span class="line">    <span class="built_in">Reflect</span>.set(target, key, newValue, receiver)</span><br><span class="line">    <span class="keyword">const</span> depend = getDepend(target,key)</span><br><span class="line">    depend.notify()</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="Depend对象的重构"><a href="#Depend对象的重构" class="headerlink" title="Depend对象的重构"></a>Depend对象的重构</h3><p>目前还存在两个问题：</p>
<ol>
<li><p>如果一个函数用到了多次同一个 <code>key</code> ，那么这个函数会被收集两次。</p>
</li>
<li><p>我们并不希望将 <code>activeReactiveFn</code> 放到 <code>get</code> 捕获器中，它是属于 <code>Depend</code> 对象的行为。</p>
</li>
</ol>
<p>解决问题一：使用 <code>Set</code> 对象，而非数组。</p>
<p>解决问题二：修改原有 <code>addDepend</code> 方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Depend</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="comment">// 使用Set对象</span></span><br><span class="line">    <span class="built_in">this</span>.reactiveFns = <span class="keyword">new</span> <span class="built_in">Set</span>()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 添加依赖</span></span><br><span class="line">  <span class="function"><span class="title">addDepend</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (activeReactiveFn) &#123;</span><br><span class="line">      <span class="built_in">this</span>.reactiveFns.add(activeReactiveFn)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 做出响应</span></span><br><span class="line">  <span class="function"><span class="title">notify</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.reactiveFns.forEach(<span class="function"><span class="params">fn</span> =&gt;</span> &#123;</span><br><span class="line">      fn()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="创建响应式对象"><a href="#创建响应式对象" class="headerlink" title="创建响应式对象"></a>创建响应式对象</h3><p>目前我们的响应式仅仅针对于 <code>obj</code> ，我们可以将响应式提取出一个函数，用于将所有对象都变成响应式对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj,&#123;</span><br><span class="line">    <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> depend = getDepend(target,key)</span><br><span class="line">      depend.addDepend()</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, receiver</span>)</span> &#123;</span><br><span class="line">      <span class="built_in">Reflect</span>.set(target, key, newValue, receiver)</span><br><span class="line">      <span class="keyword">const</span> depend = getDepend(target,key)</span><br><span class="line">      depend.notify()</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，我们的响应式实现完毕，<code>Vue3</code> 就是基于 <code>Proxy</code> 与 <code>Reflect</code> 来实现的这种响应式功能。</p>
<p>完整代码如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> activeReactiveFn = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> targetMap = <span class="keyword">new</span> <span class="built_in">WeakMap</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 普通</span></span><br><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&#x27;curry&#x27;</span>,</span><br><span class="line">  <span class="attr">age</span>: <span class="number">33</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Depend</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="comment">// this.reactiveFns = []</span></span><br><span class="line">    <span class="built_in">this</span>.reactiveFns = <span class="keyword">new</span> <span class="built_in">Set</span>()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="title">addDepend</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (activeReactiveFn) &#123;</span><br><span class="line">      <span class="built_in">this</span>.reactiveFns.add(activeReactiveFn)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="title">notify</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.reactiveFns.forEach(<span class="function"><span class="params">fn</span> =&gt;</span> &#123;</span><br><span class="line">      fn()</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监视</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">watchFn</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  activeReactiveFn = fn</span><br><span class="line">  fn()</span><br><span class="line">  activeReactiveFn = <span class="literal">null</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取依赖集</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getDepend</span>(<span class="params">target, key</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> map = targetMap.get(target)</span><br><span class="line">  <span class="keyword">if</span> (!map) &#123;</span><br><span class="line">    map = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">    targetMap.set(target, map)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> depend = map.get(key)</span><br><span class="line">  <span class="keyword">if</span> (!depend) &#123;</span><br><span class="line">    depend = <span class="keyword">new</span> Depend()</span><br><span class="line">    map.set(key, depend)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> depend</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 实现对象的响应式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, &#123;</span><br><span class="line">    <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> &#123;</span><br><span class="line">      <span class="keyword">const</span> depend = getDepend(target, key)</span><br><span class="line">      depend.addDepend()</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="function"><span class="title">set</span>(<span class="params">target, key, newValue, receiver</span>)</span> &#123;</span><br><span class="line">      <span class="built_in">Reflect</span>.set(target, key, newValue, receiver)</span><br><span class="line">      <span class="keyword">const</span> depend = getDepend(target, key)</span><br><span class="line">      depend.notify()</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> objProxy = reactive(obj)</span><br><span class="line"></span><br><span class="line">watchFn(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(objProxy.name, <span class="string">&#x27;name------&#x27;</span>)</span><br><span class="line">  <span class="built_in">console</span>.log(objProxy.name, <span class="string">&#x27;name------&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">watchFn(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(objProxy.age, <span class="string">&#x27;age-----&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> infoProxy = reactive(&#123;</span><br><span class="line">  <span class="attr">address</span>:<span class="string">&#x27;beijing&#x27;</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">watchFn(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(infoProxy.address, <span class="string">&#x27;address-----&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">objProxy.name = <span class="string">&#x27;hello&#x27;</span></span><br><span class="line">objProxy.age = <span class="number">30</span></span><br><span class="line"></span><br><span class="line">infoProxy.address = <span class="string">&#x27;chongqing&#x27;</span></span><br></pre></td></tr></table></figure>

<h3 id="Vue2-的响应式"><a href="#Vue2-的响应式" class="headerlink" title="Vue2 的响应式"></a>Vue2 的响应式</h3><p><code>Vue2</code> 是通过 <code>Object.defineProperty()</code> 实现的响应式。</p>
<p>我们可以将 <code>reactive()</code> 函数进行重构：</p>
<p>在传入对象时，我们可以遍历所有的 <code>key</code> ，并且通过属性存储描述符来监听属性的获取和修改。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现对象的响应式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">Object</span>.keys(obj).forEach(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> value = obj[key]</span><br><span class="line">    <span class="built_in">Object</span>.defineProperty(obj, key, &#123;</span><br><span class="line">      <span class="function"><span class="title">get</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> depend = getDepend(obj,key)</span><br><span class="line">        depend.addDepend()</span><br><span class="line">        <span class="keyword">return</span> value</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="function"><span class="title">set</span>(<span class="params">newValue</span>)</span> &#123;</span><br><span class="line">        value = newValue</span><br><span class="line">        <span class="keyword">const</span> depend = getDepend(obj,key)</span><br><span class="line">        depend.notify()</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">  <span class="keyword">return</span> obj</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在本章节，我们介绍了 <code>Proxy</code> 、<code>Reflect</code> 以及响应式原理。一步步从零开始，将ES6中新增的一些特性结合起来，实现了对对象的响应式。同时回顾了 <code>Vue2</code> 和 <code>Vue3</code> 响应式的不同实现。</p>

  </div>
</article>



        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">首页</a></li>
         
          <li><a href="/tags/">标签</a></li>
         
          <li><a href="/categories/">分类</a></li>
         
          <li><a href="/archives/">归档</a></li>
         
          <li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li>
         
          <li><a href="/about/">关于</a></li>
         
          <li><a href="/search/">搜索</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A1"><span class="toc-number">1.</span> <span class="toc-text">监听对象</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Proxy"><span class="toc-number">2.</span> <span class="toc-text">Proxy</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E5%AE%9E%E7%8E%B0%E7%9B%91%E5%90%AC"><span class="toc-number">2.1.</span> <span class="toc-text">Proxy实现监听</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E7%9A%84%E6%8D%95%E8%8E%B7%E5%99%A8"><span class="toc-number">2.2.</span> <span class="toc-text">Proxy的捕获器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Reflect"><span class="toc-number">3.</span> <span class="toc-text">Reflect</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="toc-number">3.1.</span> <span class="toc-text">Reflect的作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E4%B8%AD%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-number">3.2.</span> <span class="toc-text">Reflect中的方法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">3.3.</span> <span class="toc-text">Reflect的使用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Proxy%E4%B8%8EReflect%E4%B8%AD%E7%9A%84Receiver%E7%9A%84%E4%BD%9C%E7%94%A8"><span class="toc-number">3.4.</span> <span class="toc-text">Proxy与Reflect中的Receiver的作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Reflect%E4%B8%AD%E7%9A%84construct"><span class="toc-number">3.5.</span> <span class="toc-text">Reflect中的construct</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.</span> <span class="toc-text">响应式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.1.</span> <span class="toc-text">什么是响应式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E5%87%BD%E6%95%B0%E8%AE%BE%E8%AE%A1"><span class="toc-number">4.2.</span> <span class="toc-text">响应式函数设计</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E5%87%BD%E6%95%B0%E5%AE%9E%E7%8E%B0"><span class="toc-number">4.3.</span> <span class="toc-text">响应式函数实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%8D%E5%BA%94%E5%BC%8F%E4%BE%9D%E8%B5%96%E7%9A%84%E6%94%B6%E9%9B%86"><span class="toc-number">4.4.</span> <span class="toc-text">响应式依赖的收集</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%91%E5%90%AC%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%8F%98%E5%8C%96"><span class="toc-number">4.5.</span> <span class="toc-text">监听对象的变化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86"><span class="toc-number">4.6.</span> <span class="toc-text">对象的依赖管理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%BE%9D%E8%B5%96%E7%AE%A1%E7%90%86%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="toc-number">4.7.</span> <span class="toc-text">对象的依赖管理的实现</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Depend%E5%AF%B9%E8%B1%A1%E7%9A%84%E9%87%8D%E6%9E%84"><span class="toc-number">4.8.</span> <span class="toc-text">Depend对象的重构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E5%93%8D%E5%BA%94%E5%BC%8F%E5%AF%B9%E8%B1%A1"><span class="toc-number">4.9.</span> <span class="toc-text">创建响应式对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue2-%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">4.10.</span> <span class="toc-text">Vue2 的响应式</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">5.</span> <span class="toc-text">总结</span></a></li></ol>
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&text=响应式原理"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&is_video=false&description=响应式原理"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=响应式原理&body=Check out this article: https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&title=响应式原理"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&name=响应式原理&description="><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://danyuan30.gitee.io/2022/02/05/JavaScript/reactive/&t=响应式原理"><i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

    <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> 菜单</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> 目录</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> 分享</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> 返回顶部</a>
    </div>

  </div>
</div>

        
        <footer id="footer">
  <div class="footer-left">
    Copyright &copy;
    
    
    2021-2022
    柯里的语法糖
  </div>
  <div class="footer-right">
    <nav>
      <ul>
        <!--
       --><li><a href="/">首页</a></li><!--
     --><!--
       --><li><a href="/tags/">标签</a></li><!--
     --><!--
       --><li><a href="/categories/">分类</a></li><!--
     --><!--
       --><li><a href="/archives/">归档</a></li><!--
     --><!--
       --><li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li><!--
     --><!--
       --><li><a href="/about/">关于</a></li><!--
     --><!--
       --><li><a href="/search/">搜索</a></li><!--
     -->
      </ul>
    </nav>
  </div>
</footer>

    </div>
    <!-- styles -->



  <link rel="preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'"/>


    <!-- jquery -->
 
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" crossorigin="anonymous"></script> 




<!-- clipboard -->

  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.7/clipboard.min.js" crossorigin="anonymous"></script> 
  
  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"复制到粘贴板!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight table").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      text: function(trigger) {
        return Array.from(trigger.nextElementSibling.querySelectorAll('.code')).reduce((str,it)=>str+it.innerText+'\n','')
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "复制成功!");
      e.clearSelection();
    })
  })
  </script>


<script src="/js/main.js"></script>

<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

<!-- Cloudflare Analytics -->

<!-- Umami Analytics -->

<!-- Disqus Comments -->

<!-- utterances Comments -->

</body>
</html>
